package com.googlecode.connectlet.machine.provider.signature;

import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;

import com.googlecode.connectlet.crypto.digest.SignatureStream;
import com.googlecode.connectlet.machine.provider.MachineKey;
import com.googlecode.connectlet.machine.provider.MachineProvider;
import com.googlecode.connectlet.machine.provider.key.MachinePrivateKey;

public abstract class MachineSignature extends SignatureSpi {
	private AlgorithmParameters thisParams = null;
	private SignatureStream stream = null;

	protected abstract SignatureStream getSignStream(MachinePrivateKey privateKey,
			MachineKey machineKey);

	{
		try {
			thisParams = AlgorithmParameters.
					getInstance("Machine", MachineProvider.getInstance());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Deprecated
	@Override
	protected Object engineGetParameter(String param) {
		return engineGetParameters();
	}

	@Override
	protected AlgorithmParameters engineGetParameters() {
		return thisParams;
	}

	@Deprecated
	@Override
	protected void engineSetParameter(String param,
			Object value) throws InvalidParameterException {
		if (value instanceof AlgorithmParameterSpec) {
			try {
				engineSetParameter((AlgorithmParameterSpec) value);
			} catch (InvalidAlgorithmParameterException e) {
				throw new InvalidParameterException(e.getMessage());
			}
		} else {
			throw new InvalidParameterException("AlgorithmParameterSpec required");
		}
	}

	@Override
	protected void engineSetParameter(AlgorithmParameterSpec
			params) throws InvalidAlgorithmParameterException {
		if (params instanceof MachineKey) {
			try {
				thisParams.init(params);
			} catch (InvalidParameterSpecException e) {
				throw new InvalidAlgorithmParameterException(e);
			}
		} else {
			throw new InvalidAlgorithmParameterException("MachineParameterSpec required");
		}
	}

	@Override
	protected void engineUpdate(byte b) {
		stream.write(b);
	}

	@Override
	protected void engineUpdate(byte[] b, int off, int len) {
		stream.write(b, off, len);
	}

	@Override
	protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
		if (privateKey instanceof MachinePrivateKey) {
			try {
				stream = getSignStream((MachinePrivateKey) privateKey,
						thisParams.getParameterSpec(MachineKey.class));
			} catch (Exception e) {
				throw new InvalidKeyException(e);
			}
		} else {
			throw new InvalidKeyException("MachinePrivateKey required");
		}
	}

	@Override
	protected byte[] engineSign() throws SignatureException {
		byte[] result = stream.sign();
		stream.reset();
		return result;
	}

	@Deprecated
	@Override
	protected void engineInitVerify(PublicKey publicKey) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	protected boolean engineVerify(byte[] sigBytes) {
		throw new UnsupportedOperationException();
	}
}